#!/usr/bin/env python
# encoding: utf-8


"""
@time: 2016/11/21 上午10:15
"""
from functools import reduce

# 已知不等式的解集求参数的值
from mathsolver.functions.base import *
from mathsolver.functions.budengshi import common_opers as co
from sympy import S, solve, degree_list, solveset
from sympy.core.numbers import Infinity
from mathsolver.functions.budengshi.budengshi_consts import BuDengShiZuSearchKey
from operator import itemgetter


def get_solve_values(solve_set):
    """
    解集的值
    :param solve_set:
    :return:
    """

    def interval_values(s):
        vs = []
        left = solve_set.left
        right = solve_set.right
        if left != -S.Infinity:
            vs.append(left)
        if right != S.Infinity:
            vs.append(right)
        return vs

    if isinstance(solve_set, Interval):
        return interval_values(solve_set)
    else:
        s_vs = []
        intl1, intl2 = solve_set.args
        s_vs.extend(interval_values(intl1))
        s_vs.extend(interval_values(intl2))


# 参数个数小于小于等于2
# 将不等式的解集带入到原不等式,联立方程求解
class QiuCanShuZhi001(BaseFunction):
    def solver(self, *args):
        # ---------------前置处理步骤 Begin--------------------
        # ---------------前置处理步骤 End----------------------
        ineq = args[0]  # 原不等式
        ineq_solveset = args[1]  # 解集
        l, o, r = ineq.sympify()
        f = l - r
        symbs = list(f.free_symbols)
        if len(symbs) != 3:
            raise Exception('Match Type Error')
        vs = get_solve_values(ineq_solveset)
        if len(vs) != 2:
            raise Exception('Match Type Error')
        if isinstance(ineq_solveset, Interval):
            v1 = ineq_solveset.left
            v2 = ineq_solveset.right
        else:
            v1 = ineq_solveset.args[0].right
            v2 = ineq_solveset.args[1].left
        x_symbol = sympify('x')
        f1 = f.subs(x_symbol, v1)
        f2 = f.subs(x_symbol, v2)
        # 如果f1, f2 都含有一个参数, 那么就不用联立方程组
        eqs = BaseEqs([[f1, 0], [f2, 0]])
        params_vs = co.solve_poly_eqs(eqs)
        params_vs_d = {}
        for p, v in params_vs:
            if p in params_vs_d:
                params_vs_d[p] += FiniteSet(v)
            else:
                params_vs_d[p] = FiniteSet(v)
        self.output.append(BaseSymbolValue(params_vs_d))
        return self


# 参数个数等于1
class QiuCanShuZhi002(BaseFunction):
    def param_intl(self, param_symb):
        """
        是否含有参数的范围, 如果有则返回，否则返回空
        :param param_symb:
        :return:
        """
        if not self.search(BuDengShiZuSearchKey):
            return None
        ineqs = self.search(BuDengShiZuSearchKey)
        param_intls = []
        for tmp_ineq in ineqs:
            ineq_symbs = co.ineqs_symbs([tmp_ineq, ])
            if len(ineq_symbs) == 1 and sympify(ineq_symbs[0]) == sympify(param_symb):
                param_intls.append(solveset(''.join(map(str, tmp_ineq)), domain=S.Reals))
        if not param_intls:
            return None
        else:
            return co.intervals_intersect(param_intls)

    def solver(self, *args):
        # ---------------前置处理步骤 Begin--------------------
        # ---------------前置处理步骤 End----------------------
        self.label.add('已知不等式解集求参数')
        ineq = args[0]  # 原不等式
        ineq_solveset = args[1]  # 解集
        l, o, r = ineq.sympify()
        f = l - r
        symbs = list(f.free_symbols)
        if len(symbs) != 2:
            raise Exception('Match Type Error')
        x_symbol = sympify('x')
        param_symb = list(FiniteSet(*symbs) - FiniteSet(sympify('x')))[0]
        abs_l_value, abs_r_value = abs(ineq_solveset.left), abs(ineq_solveset.right)
        if not isinstance(abs_l_value, Infinity) and not isinstance(abs_r_value, Infinity) \
                and ineq_solveset.left.is_real and ineq_solveset.right.is_real:  # 如果有两个接
            f1 = f.subs(x_symbol, ineq_solveset.left)
            f2 = f.subs(x_symbol, ineq_solveset.right)
            eqs = BaseEqs([[f1, '0'], [f2, '0']])
            self.steps.append(['由题意可知', eqs.printing()])
            param_values = map(itemgetter(1), map(itemgetter(0), co.isolve_eqs(eqs)))
            r_print = '求得' + new_latex(param_symb) + '值为:'
            for index, parm_v in enumerate(param_values):
                if index == 0:
                    r_print += new_latex(parm_v)
                else:
                    r_print += '或' + new_latex(parm_v)
            param_symb_intl = self.search(param_symb)
            self.steps.append([r_print, ''])
            if param_symb_intl and isinstance(param_symb_intl, Interval):  # 如果有参数的范围
                param_values = filter(lambda _: _ in param_symb_intl, param_values)
                self.steps.append(['因为' + new_latex(param_symb), '\\in ' + new_latex(param_symb_intl)])
                r_print = '求得' + new_latex(param_symb) + '值为:'
                for index, parm_v in enumerate(param_values):
                    if index == 0:
                        r_print += new_latex(parm_v)
                    else:
                        r_print += '或' + new_latex(parm_v)
                self.steps.append(['所以求得' + r_print, ''])
            self.output.append(BaseSymbolValue({param_symb: param_values}))
            return self
        # l_value, r_value = ineq_solveset.left, ineq_solveset.right
        solve_value = ineq_solveset.left if not isinstance(abs_l_value, Infinity) else ineq_solveset.right
        f_sub = f.subs(x_symbol, solve_value)
        self.steps.append(['由题意可知 %s=0' % new_latex(f_sub), ''])
        param_values = solveset(f_sub, domain=S.Reals)
        self.steps.append(['求得%s的值为' % new_latex(param_symb), new_latex(FiniteSet(*param_values))])
        validate_param_values = []
        if self.param_intl(param_symb):
            param_intl = self.param_intl(param_symb)
            intl_print = co.print_interval(param_intl, param_symb)
            self.steps.append(['因为' + intl_print, ''])
            for v in param_values:
                if v in param_intl:
                    validate_param_values.append(v)
        # param_value = JieBuDengShi().solver()
        self.steps.append(['求得%s=%s' % (new_latex(param_symb), new_latex(FiniteSet(*validate_param_values))), ''])
        self.output.append(BaseSymbolValue({param_symb: validate_param_values}))
        return self


# 参数个数为3的
class QiuCanShuZhi003(BaseFunction):
    def solver(self, *args):
        # ---------------前置处理步骤 Begin--------------------
        # ---------------前置处理步骤 End----------------------
        self.label.add('已知不等式解集求参数')
        ineq = args[0]  # 原不等式
        ineq_solveset = args[1]  # 解集
        l, o, r = ineq.sympify()
        f = l - r
        symbs = list(f.free_symbols)
        paramers = list(FiniteSet(*symbs) - FiniteSet(sympify('x')))
        if len(paramers) != 3:
            raise Exception('Match Type Error')
        if isinstance(ineq_solveset, Interval):
            v1 = ineq_solveset.left
            v2 = ineq_solveset.right
        else:
            v1 = ineq_solveset.args[0].right
            v2 = ineq_solveset.args[1].left
        x_symbol = sympify('x')
        f1 = f.subs(x_symbol, v1)
        f2 = f.subs(x_symbol, v2)
        eqs = BaseEqs([[f1, 0], [f2, 0]])
        self.steps.append(['联立方程组' + eqs.printing(), ' 得'])
        a_symbol, b_symbol, _ = map(sympify, sorted(map(str, paramers)))
        a_b_solve = solve([f1, f2], a_symbol, b_symbol)
        solve_eqs = BaseEqs(a_b_solve.items())
        self.steps.append([solve_eqs.printing(), ''])
        self.output.append(BaseSymbolValue(a_b_solve))
        return self


# 所有未知参数个数大于解的个数
class QiuCanShuZhi004(BaseFunction):
    def solver(self, *args):
        self.label.add('已知不等式解集求参数')
        # ---------------前置处理步骤 Begin--------------------
        # ---------------前置处理步骤 End----------------------
        ineq = args[0]  # 原不等式
        ineq_solveset = args[1]  # 解集
        solves_vs = get_solve_values(ineq_solveset)
        l, o, r = ineq.sympify()
        f = l - r
        x_symbol = sympify('x')
        exprs = list(map(lambda v: f.subs(x_symbol, v), solves_vs))
        symbs = list(f.free_symbols)
        paramers = list(FiniteSet(*symbs) - FiniteSet(sympify('x')))
        paramers = list(map(sympify, sorted(map(str, paramers))))
        paramers_solve = solve(exprs, *paramers[:-1])
        if len(exprs) == 1:
            self.steps.append(['由题意可知 ' + BaseEq([exprs[0], 0]).printing(), '得'])
            result_eq = BaseEq(list(paramers_solve.items())[0])
            self.steps.append([result_eq.printing(), ''])
            self.output.append(BaseSymbolValue(paramers_solve))
            return self
        else:
            solve_eqs = BaseEqs(map(lambda ep: [ep, 0], exprs))
            self.steps.append(['联立方程组 ' + solve_eqs.printing(), ''])
            result_eqs = BaseEqs(paramers_solve.items())
            self.steps.append([result_eqs.printing(), ''])
            self.output.append(BaseSymbolValue(paramers_solve))
            return self


def detect_param_intl(ineq, solve_set, x_symbol='x'):
    """
    根据不等式的解集以及不等式符号来决定最高次系数的正负
    :param ineq:
    :param solve_set:
    :param x_symbol: x的符号变量
    :return:
    """
    l_expr, ineq_op, r_expr = ineq.sympify()
    f = l_expr - r_expr
    x_symbol = sympify(x_symbol)
    x_high_degree = degree_list(f, x_symbol)[0]
    x_high_coef = f.coeff(x_symbol ** x_high_degree)
    if x_high_degree == 2:
        if ineq_op.find('>') >= 0 and isinstance(solve_set, Interval):  # 开口方向向下,系数小于0
            return x_high_coef, Interval(-S.Infinity, 0)
        elif ineq_op.find('>') >= 0 and isinstance(solve_set, Union):  # 开口方向向上，系数大于0
            return x_high_coef, Interval(0, S.Infinity)
        elif ineq_op.find('<') >= 0 and isinstance(solve_set, Interval):  # 开口方向向上，系数大于0
            return x_high_coef, Interval(0, S.Infinity)
        else:  # 开口方向向下,系数小于0
            return x_high_coef, Interval(-S.Infinity, 0)
    elif x_high_degree == 1:
        if solve_set.is_subset(Interval(0, S.Infinity)) and ineq_op.find('>') >= 0:
            return x_high_coef, Interval(0, S.Infinity)
        elif solve_set.is_subset(Interval(-S.Infinity, 0)) and ineq_op.find('<') >= 0:
            return x_high_coef, Interval(0, S.Infinity)
        else:
            return x_high_coef, Interval(-S.Infinity, 0)
    else:
        raise Exception('Type Match Error')


# 求参数,没有给出具体的解, 但是给出了解得关系
# Input paramer1:不等式; paramer2:解的关系
# 关于x的不等式{x^2} - 2ax - 8{a^2} < 0 (a > 0 )的解集为 ({x_1},{x_2} ),{x_2} - {x_1} = 15,则a =  ()
class QiuCanShuZhi005(BaseFunction):
    def solver(self, *args):
        self.label.add('已知不等式解集求参数')
        arg1, arg2 = args
        _l, _op, _r = arg1.sympify()
        f = _l - _r
        x_symbol = sympify('x')
        param_symb = list(set(f.free_symbols) - {x_symbol})[0]
        x_a_values = co.isolve_eq2(BaseEq([f, 0]), x_symbol)
        x_a_values = filter(lambda _: reduce(lambda _1, _2: _1 and _2, _), x_a_values)
        x_values = set(map(itemgetter(0), x_a_values))
        x1_v, x2_v = x_values
        x1, x2 = map(sympify, co.eqs_symbs([arg2.sympify()]))
        self.steps.append(['由题意可知 :', BaseEq([x1, x1_v]).printing() + ' ' + BaseEq([x2, x2_v]).printing()])
        e_l, e_r = arg2.sympify()
        x_subs = ((x1, x1_v), (x2, x2_v))
        n_eq = BaseEq([abs(e_l.subs(x_subs)), abs(e_r.subs(x_subs))])
        self.steps.append(['所以:', n_eq.printing()])
        p_vs = co.isolve_eq2(n_eq)
        self.steps.append(['求得:' + new_latex(param_symb), '值为:' + new_latex(FiniteSet(*p_vs))])
        param_symb_intl = self.search(param_symb)
        if param_symb_intl:
            self.steps.append(['因为:' + new_latex(param_symb), ' \\in ' + new_latex(param_symb_intl)])
            p_vs = filter(lambda _: _ in param_symb_intl, p_vs)
            r_print = '所以' + new_latex(param_symb) + '值为:'
            for index, p_v in enumerate(p_vs):
                if index == 0:
                    r_print += new_latex(p_v)
                else:
                    r_print += '或' + new_latex(p_v)
            self.steps.append([r_print, ''])
        self.output.append(BaseSymbolValue({param_symb: p_vs}))
        return self


# 已知不等式的解集求不等式中参数的值
class QiuBuDengShiCanShuZhi(BaseFunction):
    CLS = [QiuCanShuZhi001, QiuCanShuZhi002, QiuCanShuZhi003, QiuCanShuZhi004, QiuCanShuZhi005]

    def solver(self, *args):
        solve_r = None
        tmp_args = [arg for arg in args]
        if isinstance(tmp_args[1], BaseSet):
            tmp_args[1] = co.base_set_2_sympy_set(tmp_args[1])
        if isinstance(tmp_args[1], BaseInter):
            tmp_args[1] = tmp_args[1].interval
        x_high_coef, coef_intl = None, None
        try:
            x_high_coef, coef_intl = detect_param_intl(tmp_args[0], tmp_args[1])
        except Exception:
            pass
        for cl in QiuBuDengShiCanShuZhi.CLS:
            try:
                solve_r = cl(verbose=True)
                solve_r.known = self.known
                solve_r = solve_r.solver(*tmp_args)
                if x_high_coef:
                    coef_intl_dict = {(str(x_high_coef) + '_domain'): coef_intl}
                    solve_r.output.append(BaseSymbolValue(coef_intl_dict))
                break
            except Exception:
                pass
        if not solve_r or not solve_r.output:
            raise Exception('Can not solve the question')
        solve_r.label.add('含参不等式')
        return solve_r


if __name__ == '__main__':
    # QiuBuDengShiCanShuZhi(verbose=True).solver(BaseIneq(['a*x**2 + 5*x - 2', '>', '0']), Interval(sympify('1/2'), 3))
    # print QiuBuDengShiCanShuZhi(verbose=True).solver(BaseIneq(['a - 3*x', '>', '0']),
    #                                                  Interval(-S.Infinity, 1)).output[0].sympify()
    # r2 = QiuBuDengShiCanShuZhi(verbose=True).solver(BaseIneq(['6**x + 4**x + a*9**x', '>=', '0']),
    # Interval(-S.Infinity, 1))
    # print r2.output[0].sympify()
    # print QiuBuDengShiCanShuZhi(verbose=True).solver(BaseIneq(['a*x**2 + b*x + c', '>', '0']),
    #                                                  Interval(2, 3)).output[0].sympify()
    # _r = QiuBuDengShiCanShuZhi(known={sympify('a'): Interval(1, S.Infinity, left_open=True)}).solver(
    #     BaseIneq(['Abs(2*Abs(x) - 2*Abs(x - a))', '<=', '2']),
    #     Interval(1, 2))

    # _r = QiuBuDengShiCanShuZhi(known={sympify('a'): Interval(1, S.Infinity, left_open=True)}).solver(
    #     BaseIneq(['Abs(k*x - 4)', '<=', '2']),
    #     Interval(1, 3))
    # for s1, s2 in _r.steps:
    #     print s1, s2
    # _solve = QiuBuDengShiCanShuZhi(known={sympify('a'): Interval(0, S.Infinity, left_open=True)}).solver(
    #     BaseIneq(['x**2 - 2*a*x - 8*a**2', '<', '0']), BaseEq(['x1- x2', '15']))

    # _solve = QiuBuDengShiCanShuZhi().solver(
    #     BaseIneq(['Abs(a*x + 1)', '<=', '3']), Interval(-2, 1))
    # _solve = QiuBuDengShiCanShuZhi().solver(
    #     BaseIneq(['Abs(x - a)', '<=', '3']), Interval(-1, 5))
    _solve = QiuBuDengShiCanShuZhi().solver(
        BaseIneq(['a*x**2 + b*x + c', '<', '0']), Interval(-S.Infinity, -1).union(Interval(sympify('1/2'), S.Infinity)))
